#ifndef _TILEMGR1_H
#define _TILEMGR1_H

#define NODATA 0xFF
#define NOTILE 0xFFFFFFFF
#define NOTILE64 0xFFFFFFFFFFFFFFFFU

#include "TerrainRender.h"

#define TERRAIN_TEXTURE_FOLDER "Terrain\\"
//#define TILE_NVTX 66U//18U
//#define TILE_VTXCNT (TILE_NVTX+1)*(TILE_NVTX+1)// 1225U//361U
//#define TILE_VTXCNT_R ((TILE_NVTX+1)*(TILE_NVTX+1) - ((TILE_NVTX+1)*TILE_NVTX)/2)// 630U//190U
//#define TILE_IDXCNT 6*TILE_NVTX*TILE_NVTX		// 6936U//1944U
//#define TILE_IDXCNT_R TILE_NVTX*TILE_NVTX*3		//3468U//972U

//TILEDESC1 flags:
#define SPECULAR	2U
#define LIGHTS		4U
#define LOAD_NORM	8U
#define LOAD_HEIGHT 16U
#define LOAD_LCOVER 32U
#define OLDIDX		64U
#define LOAD_TEX	128U
#define RECOMPUTEBS	256U
#define REFRESH_TEX	512U

#define MAX_RESOLUTION 18	//maximum tile mesh resolution

#define HMAP_SIDE 65U
#define HMAP_NPIXEL 4225U
#define HMAP_DIM 64.0f
#define HMAP_DIF 0.015625f

#define LCMAP_SIDE 65U
#define LCMAP_NPIXEL 4225U

void MatrixRotY1( D3DXMATRIX *out, float angle );
void read_callback( png_structp png_ptr, png_bytep to_lib, png_size_t lenght );
UINT8 GetMonth( double MJD );

//======================================
//		File headers
//======================================
#pragma region file headers

#pragma pack( push, 2 )
struct HMAPFILESPEC {
	UINT64 hidx;
	DWORD flags;
	DWORD subidx[4];
};
#pragma pack( pop )

//24 byte
struct LCOVERFILESPEC {
	DWORD lidx;
	DWORD flags;
	DWORD subidx[4];
};

#pragma endregion

class TileLoader;

//============================================================================
//							TerrainManager
//============================================================================

class TerrainManager {
	friend class TileRenderMgr;
	friend class DetailsManager;
	friend class TileLoader;

	typedef struct RENDERPARAMS {
		D3DXMATRIX mWorld_tmp;
		D3DXMATRIX mWorld;
		MATRIX3 grot;
		VECTOR3 cdir, cpos, sdir;
		double viewap;
		double dist_scale;
		UINT8 tgtlevel;
		double cdist, clng, clat, crad;
		bool bFog;

		//?
		bool bCockpit;
		double objsize;
		double horzdist;
	} RENDERPARAMS;

public:
	TerrainManager( OBJHANDLE _obj, vPlanet *_OBJ );
	~TerrainManager();

	static void GlobalInit();
	static void GlobalExit();

	//render:
	void SaveParams( D3DXMATRIX *wmat, double scale, UINT level, double viewap = 0.0, bool bfog = false );
	void Render();

	//sets default water microtexture:
	void SetWaterMicroTexture( const char *fname );

	void UnloadTextures();

private:
	//===================================
	//	Resource loading
	//===================================
	void FindResources();
	void LoadTextures();
	void LoadSpecMask();
	void LoadNormalMap();
	void LoadHeightMap();
	void LoadLandCoverMap();

	void AddSubTileData( TILEDESC1 &td, TILEFILESPEC *tfs, DWORD idx, DWORD sub, DWORD level, DWORD tilever );
	void AddSubTileNormalData( TILEDESC1 &td, TILEFILESPEC *tfs, DWORD idx, DWORD sub, DWORD level, DWORD tilever );
	void AddSubTileHeightData( TILEDESC1 &td, HMAPFILESPEC *hfs, DWORD idx, DWORD sub, DWORD level );
	void AddSubTileLandCoverData( TILEDESC1 &td, LCOVERFILESPEC *lfs, DWORD idx, DWORD sub, DWORD level );
	
	DWORD LoadTextures( char *fname, DWORD ntex, ID3D11ShaderResourceView **out );
	
	DWORD LoadL8HeightData();

	DWORD LoadL8HeightData( char *fname, HMAPFILESPEC *hfs );
	DWORD LoadL8LandCoverData( char *fname, LCOVERFILESPEC *lfs );

	//===================================
	//	Tile new/delete
	//===================================

	TILEDESC1 *NewTile( float amin, float amax );
	void ChangeTextureSet( UINT8 month );	//for multiple texture set of planets.(month-specific)
	void DeleteSubTiles( TILEDESC1 *tile );	//deletes all void subtiles of a tile
	bool DeleteTile( TILEDESC1 *tile );		//deletes a tile and its subtile if tile has no texture

	//===================================
	//	Every frame methods
	//===================================
	void ExpandTRSBuffer();
	void ExpandDetailsTRSBuffer();

	void ProcessTile(
		UINT8 hemisphere, UINT8 level, UINT ilat, UINT nlat, UINT ilng, UINT nlng, TILEDESC1 *td,
		TILECRDRANGE &RANGE, TILETEX Tex, DWORD flag,
		TILECRDRANGE &bkp_RANGE, TILETEX bkp_Tex, DWORD bkp_flag );

	//misc:
	void TileWorldMatrix( UINT ilat, UINT nlat, UINT ilng, UINT nlng, D3DXMATRIX &mWorld );
	void ComputeTileBoundingSphere( TILEDESC1 *tile, UINT8 hemisphere, UINT ilat, UINT nlat, UINT nlng );
	bool IsTileVisible( TILEDESC1 *tile, D3DXMATRIX *mWorld, float dist_scale );
	
	//=================================================
	//			------------------------
	//=================================================

	TileRenderMgr *rend;		//renders tiles to main RT using shadow volume texture
	DetailsManager *det;		//renders details over tiles rendered by TileRenderMgr to main RT

	static TileLoader *tload;			//texture loading from files

	static UINT patchidx[9], NLAT[9], NLNG5[1], NLNG6[2], NLNG7[4], NLNG8[8], *NLNG[9];
	static D3DXMATRIX RSouth, mWorld_north;
	static MATRIX3 grot_north;
	static TILERENDERSPEC
		*TileList,	//for shadows and tile rendering
		*DetailsTileList;//for detail rendering (should be passed into DetailsManager)
		
	static UINT
		ntile, tbufsize,
		ntile_det, tbufsize_det;

	OBJHANDLE obj;	//orbiter handle for the planet
	vPlanet *OBJ;	//ptr to D3D11Client visual object
	char *objname;	//name of the planet
	RENDERPARAMS RP;
	VECTOR3 pcdir;//allows reduce resolution level when camera moves fast

	std::vector<TILEDESC1*> buf;
	TILEDESC1
		*tiledesc/*,	//L1-L8 tiles
		**buf*/;		//L9-L18 tiles
	//UINT nbuf, bsize;		//number of tiles in buffer, buffer size

	//tile texture resources:
	ID3D11ShaderResourceView
		**texbuf,	//L1-L8 diffuse textures
		**specbuf,	//L1-L8 specular textures
		**normbuf,	//L1-L8 normal textures
		**heightbuf,//L8 heightmaps
		**lcoverbuf;//L8 land cover maps

	UINT
		ntex,		//number of L1-L8 diffuse textures
		nspec,		//number of L1-L8 specular textures
		nnorm,		//number of L1-L8 normal maps
		nheight,	//number of L8 heightmaps
		nlcover,	//number of L8 land cover textures

		nhitex,		//number of L >= L9 diffuse textures
		nhispec,	//number of L >= L9 specular textures
		nhinorm,	//number of L >= L9 normal textures
		nhiheight,	//number of L >= L9 heightmaps
		nhilcover,	//number of L >= L9 land cover maps

		maxlvl,		//maximum tile mesh resolution
		maxbaselvl;	//min( 8, maxlvl )

	//=================================================
	//			Configuration settings
	//=================================================

	PlanetaryConfig *CFG;

	bool
		L8HeightMapGenerated;
	float
		*FlatDispMicroTexData,
		*CoarseDispMicroTexData,
		DisplacementMicroTexDim,
		HeightScale;

	char
		*tex_fname,				//Planet_tile.tex
		*spec_fname,			//Planet_lmask_tile.tex
		*norm_fname,			//Planet_norm.tex
		*height_fname,			//Planet_hmap.tex
		*lcover_fname,			//Planet_lcover.tex
		*tex_fname_month[12];	//filenames for season/month textures

	//terrain shadows
	std::vector<TILERENDERSPEC>
		tiles_noculling,
		tiles_shadows,
		tiles_camera;

	void CreateShadowsTileList();
	void CreateCameraTileList();
public:
	TileRenderMgr * GetTileRenderMgr() { return rend; }

	void RenderTerrainShadows();
};

//============================================================================
//				Loading of textures
//============================================================================

#define MAXLOADQUEUE 10

class TileLoader {
	friend class TerrainManager;

	typedef struct LOADINGPARAM {
		TILEDESC1 *td;
		TerrainManager *TM;
	} LOADINGPARAM;

public:
	TileLoader();
	~TileLoader();

	//creates new TILEDESC1 struct and set parent-tile bounding values
	//(bounding sphere will be updated after loading/generation of detailed heightmap)
	bool LoadTile( TerrainManager *TM, TILEDESC1 *tile );

	//loads textures, specular, normal, LCC maps and heightmaps
	static DWORD WINAPI LoadingProc( void *data );

private:
	//loading of diffuse tile texture/specular mask/normal map
	static bool LoadTexture( char *fname, UINT64 ofs, ID3D11ShaderResourceView **out );
	//65x65 PNG compressed SINT16 tiles
	static bool LoadHeightmap( char *fname, UINT64 ofs, INT16 *A, ID3D11ShaderResourceView **out );
	//65x65 PNG compressed UINT8 tiles
	static bool LoadLandCoverMap( char *fname, UINT32 ofs, ID3D11ShaderResourceView **out );

	//queue of tiles to be loaded
	static std::queue<LOADINGPARAM> queue;

	//threading vars
	HANDLE hLoading;	//loading thread handle
	DWORD dwLoading;	//loading thread ID

	static CRITICAL_SECTION LoadingCS;
};

#endif

/*
										m./px.
L1	1			64			64			626,747 m
L2	1			128			128			313,373 m
L3	1			256			256			156,686 m
L4	2			512			256			78,343 m
L5	8			1,024		512			39,171 m
L6	24			2,048		1,024		19,586 m				NMAP[L6]
L7	100			4,096		2,048		9,792 m		73.6 km		NMAP[L7]
L8	364			8,192		4,096		4,896 m		36.8 km		NMAP[L8]
L9	1,456		16,384		8,192		2,448 m		18.4 km		NMAP[L9]
L10	5,824		32,768		16,384		1,224 m		9.2 km		NMAP[L10]	GTOPO30
L11	23,296		65,536		32,768		612	m		4.6 km
L12	93,184		131,072		65,536		306 m		2.3 km
L13	372,736		262,144		131,072		153 m		1.15 km
L14	1,490,944	524,288		262,144		76 m		576 m
L15	5,963,776	1,048,576	524,288		38 m		288 m
L16	23,855,104	2,097,152	1,048,576	19 m		144 m
L17 95,420,416	4,194,304	2,097,152	9.6 m		72 m
L18	381,681,664	8,388,608	4,194,304	4.8 m		36 m

diffuse
normal
height

//Tile rendering settings:
typedef struct TILERENDERSPEC {
	TILEDESC1 *td;		//tiledesc struct
	D3DXMATRIX mWorld;	//world matrix
	TILETEX tex;
	TEXCRDRANGE1 range_tex;	//acual range of texcoords
	TEXCRDRANGE1 range_ntex;
	TEXCRDRANGE1 range_htex;
	bool top;			//true for polar tiles [reduced number of triangles => different vertex data]
	UINT8 lvl;		//required for mesh generation in shader
	UINT
		ilat, nlat, nlng;
	float cnt_dist;		//distance of tile center from camera
	float bs_dist;		//distance of bounding sphere border from camera
} TILERENDERSPEC;*/
